﻿
using System.Security.Claims;

namespace Ocelot.Admin.Api.Application
{
    public class SystemService : ISystemService, ITransient
    {
        private readonly AppInfoOptions _appInfoOptions;
        private readonly SqlSugarScopeProvider _sqlserverDB;

        public SystemService(ISqlSugarClient db, IOptionsMonitor<AppInfoOptions> optionsMonitor)
        {
            _sqlserverDB = db.AsTenant().GetConnectionScope(DatabaseConfigIdConst.OcelotSettings);
            _appInfoOptions = optionsMonitor.CurrentValue;
        }

        /// <summary>
        /// 登录授权
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<LoginResponse> LoginAsync(LoginRequest request)
        {
            UserAccount userAccount;
            if (request.SysType == 0)
            {
                if (_appInfoOptions.TotpLogin)
                {
                    userAccount = await LoginByTotpAsync(request);
                }
                else
                {
                    userAccount = await LoginByUserAccountAsync(request);
                }
            }
            else
            {
                userAccount = await LoginByOpenIdAsync(request);
                if (string.IsNullOrWhiteSpace(userAccount.Account))
                {
                    return new LoginResponse()
                    {
                        OpenId = userAccount.OpenId,
                    };
                }
            }

            var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
            {
                {"userId",userAccount.ID.ToString() },
                {"fullName",userAccount.Name },
                {"openId",userAccount.OpenId }
            });
            var refreshExpirdTime = int.TryParse(App.Configuration["JWTSettings:RefreshTokenExpiredTime"], out int intRefreshTokenExpiredTime) ? intRefreshTokenExpiredTime : 43200;
            var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, refreshExpirdTime);
            return new LoginResponse()
            {
                AccessToken = accessToken,
                RefreshToken = refreshToken,
                FullName = userAccount.Name,
                Account = userAccount.Account,
            };
        }

        /// <summary>
        /// 使用用户名密码登录
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private async Task<UserAccount> LoginByUserAccountAsync(LoginRequest request)
        {
            var userAccount = await _sqlserverDB.Queryable<UserAccount>().FirstAsync(o => o.Account == request.UserName && o.Password == request.Password);
            if (userAccount == null)
            {
                throw Oops.Bah("用户名或密码错误");
            }
            if (!userAccount.Enabled)
            {
                throw Oops.Bah("该用户已被禁用");
            }
            return userAccount;
        }

        /// <summary>
        /// 使用Totp登录
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private async Task<UserAccount> LoginByTotpAsync(LoginRequest request)
        {
            var userAccount = await _sqlserverDB.Queryable<UserAccount>().FirstAsync(o => o.Account == request.UserName);
            if (userAccount == null)
            {
                throw Oops.Bah("用户名或密码错误");
            }
            if (!userAccount.Enabled)
            {
                throw Oops.Bah("该用户已被禁用");
            }
            var otp = new Totp(OtpHashAlgorithmEnum.SHA256, 6);
            var verifyResult = otp.Verify(userAccount.OpenId, request.Password, TimeSpan.FromSeconds(60));
            if (!verifyResult)
            {
                throw Oops.Bah("动态密码无效");
            }
            return userAccount;
        }


        /// <summary>
        /// OpenId用户登录
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private async Task<UserAccount> LoginByOpenIdAsync(LoginRequest request)
        {
            if (!string.IsNullOrWhiteSpace(request.Code))
            {
                var wxUserInfo = await GetWeChatOpenIdForCodeAsync(request.Code);
                if (wxUserInfo == null)
                {
                    throw Oops.Bah("未能获取到OpenId");
                }

                if (string.IsNullOrWhiteSpace(wxUserInfo.OpenId))
                {
                    throw Oops.Oh(wxUserInfo.ErrMsg).StatusCode(wxUserInfo.Errcode);
                }
                var bindAccount = await _sqlserverDB.Queryable<UserAccount>().FirstAsync(o => o.OpenId == wxUserInfo.OpenId);
                if (bindAccount == null)
                {
                    return new UserAccount()
                    {
                        OpenId = wxUserInfo.OpenId,
                    };
                }
                if (!bindAccount.Enabled)
                {
                    throw Oops.Bah("该账号已被禁用");
                }
                return bindAccount;
            }
            else
            {
                if (string.IsNullOrWhiteSpace(request.OpenId))
                {
                    throw Oops.Bah("OpenId不能为空");
                }
                var bindAccountInfo = await _sqlserverDB.Queryable<UserAccount>().FirstAsync(o => o.Account == request.UserName && o.Password == request.Password);
                if (bindAccountInfo == null)
                {
                    throw Oops.Bah("用户名或密码错误");
                }
                if (bindAccountInfo.Enabled == false)
                {
                    throw Oops.Bah("该账号已被禁用");
                }
                if (!string.IsNullOrWhiteSpace(bindAccountInfo.OpenId) && bindAccountInfo.OpenId != request.OpenId)
                {
                    throw Oops.Bah("该账号已被绑定");
                }

                if (string.IsNullOrWhiteSpace(bindAccountInfo.OpenId))
                {
                    await _sqlserverDB.Updateable<UserAccount>().SetColumns(o => new UserAccount() { OpenId = request.OpenId }).Where(it => it.ID == bindAccountInfo.ID).ExecuteCommandAsync();
                }
                bindAccountInfo.OpenId = request.OpenId;
                return bindAccountInfo;
            }
        }

        /// <summary>
        /// 根据code用户openId
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        private async Task<WxUserInfoDto> GetWeChatOpenIdForCodeAsync(string code)
        {
#if DEBUG
            return new WxUserInfoDto
            {
                OpenId = "woshiopenid"
            };
#else
            try
            {
                string url = $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={_appInfoOptions.AppId}&secret={_appInfoOptions.AppSecret}&code={code}&grant_type=authorization_code";
                return await url.GetAsAsync<WxUserInfoDto>();
            }
            catch (Exception ex)
            {
                throw Oops.Oh(ex.Message);
            }
#endif
        }


        public TotpResponse GetTotp()
        {
            var openId = App.User?.FindFirstValue("openId");
            if (string.IsNullOrWhiteSpace(openId))
            {
                throw Oops.Bah("动态密码生成失败");
            }
            var otp = new Totp(OtpHashAlgorithmEnum.SHA256, 6);
            var (output, date) = otp.Compute(openId);
            var now = DateTime.Now;
            var countDown = (date - now).Seconds;
            if (countDown == 0)
            {
                Thread.Sleep(1000);
                var (output2, date2) = otp.Compute(openId);
                return new TotpResponse()
                {
                    CountDown = (date2 - now).Seconds,
                    TotpPwd = output2
                };
            }
            return new TotpResponse()
            {
                CountDown = countDown,
                TotpPwd = output
            };
        }
        
    }
}